#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time    : 2024/9/25 16:07
# @Author  : Tom_zc
# @FileName: pipeline_helper.py
# @Software: PyCharm
import logging

from huaweicloudsdkcloudpipeline.v2.region.cloudpipeline_region import CloudPipelineRegion
from huaweicloudsdkcodeartspipeline.v2 import CodeArtsPipelineClient, ShowPipelineRunDetailRequest, \
    ShowPipelineLogRequest, LogQuery, ShowStepOutputsRequest, CreatePipelineNewRequest, PipelineDTO, \
    PipelineConcurrencyMgmt, CodeSource, CodeSourceParams, CustomVariable, DeletePipelineRequest, \
    ListPipelinesRequest, ListPipelineQuery, UpdatePipelineInfoRequest

from huaweicloudsdkcodeartspipeline.v2.region.codeartspipeline_region import CodeArtsPipelineRegion
from huaweicloudsdkcore.auth.credentials import BasicCredentials
from huaweicloudsdkcodeartsbuild.v3.region.codeartsbuild_region import CodeArtsBuildRegion
from huaweicloudsdkcodeartsbuild.v3 import *
from huaweicloudsdkcore.exceptions import exceptions
from huaweicloudsdkcore.utils import http_utils

logging.basicConfig(level=logging.INFO,
                    format="%(asctime)s  %(levelname)s:%(message)s")


# noinspection PyProtectedMember
class PipeLineHelper:
    def __init__(self, ak, sk):
        self._credentials = BasicCredentials(ak, sk)

    def get_pipeline_run_detail(self, project_id, pipeline_id, pipeline_run_id):
        try:
            client = CodeArtsPipelineClient.new_builder() \
                .with_credentials(self._credentials) \
                .with_region(CodeArtsPipelineRegion.value_of("cn-north-4")) \
                .build()
            request = ShowPipelineRunDetailRequest()
            request.pipeline_run_id = pipeline_run_id
            request.project_id = project_id
            request.pipeline_id = pipeline_id
            response = client.show_pipeline_run_detail(request)
            return response.to_json_object()
        except exceptions.ClientRequestException as e:
            logging.info("[get_pipeline_run_detail] {}/{}/{}/{}".format(e.status_code,
                                                                        e.request_id,
                                                                        e.error_code,
                                                                        e.error_msg))
            raise Exception("get_pipeline_run_detail failed")

    def get_pipeline_log(self, project_id, pipeline_id, pipeline_run_id, job_run_id, step_run_id):
        client = CodeArtsPipelineClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CloudPipelineRegion.value_of("cn-north-4")) \
            .build()
        try:
            start_offset = 0
            log_content = str()
            while True:
                request = ShowPipelineLogRequest()
                request.project_id = project_id
                request.pipeline_id = pipeline_id
                request.pipeline_run_id = pipeline_run_id
                request.job_run_id = job_run_id
                request.step_run_id = step_run_id
                request.body = LogQuery(
                    start_offset=start_offset,
                    sort="asc",
                    limit=1000
                )
                response = client.show_pipeline_log(request)
                log_content += response.log
                if not response.has_more:
                    break
                start_offset = response.end_offset
            return log_content
        except exceptions.ClientRequestException as e:
            logging.info("[get_pipeline_log] {}/{}/{}/{}".format(e.status_code,
                                                                 e.request_id,
                                                                 e.error_code,
                                                                 e.error_msg))
            raise Exception("get_pipeline_log failed")

    def get_build_number(self, job_id, record_id):
        client = CodeArtsBuildClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CodeArtsBuildRegion.value_of("cn-north-4")) \
            .build()

        try:
            request = ShowListHistoryRequest()
            request.job_id = job_id
            request.interval = 5
            request.offset = 0
            request.limit = 100
            response = client.show_list_history(request)
            for record in response.history_records:
                if record.job_id == job_id and record.record_id == record_id:
                    return record.build_number
            raise Exception("not found the build number in {}/{}".format(job_id, record_id))
        except exceptions.ClientRequestException as e:
            logging.info("[get_build_list_history] {}/{}/{}/{}".format(e.status_code,
                                                                       e.request_id,
                                                                       e.error_code,
                                                                       e.error_msg))
            raise Exception("get_build_list_history failed")

    def get_build_record_id(self, job_id, build_number):
        client = CodeArtsBuildClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CodeArtsBuildRegion.value_of("cn-north-4")) \
            .build()

        try:
            request = ShowRecordDetailRequest()
            request.job_id = job_id
            request.build_no = build_number
            response = client.show_record_detail(request)
            return response.result.build_record_id
        except exceptions.ClientRequestException as e:
            logging.info("[get_build_record_id] {}/{}/{}/{}".format(e.status_code,
                                                                    e.request_id,
                                                                    e.error_code,
                                                                    e.error_msg))
            raise Exception("get_build_record_id failed")

    # noinspection PyProtectedMember
    def download_build_log(self, record_number):
        client = CodeArtsBuildClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CodeArtsBuildRegion.value_of("cn-north-4")) \
            .build()

        try:
            request = DownloadBuildLogRequest()
            request.record_id = record_number
            response = client.download_build_log(request)
            return response._stream.content.decode()
        except exceptions.ClientRequestException as e:
            logging.info("[download_build_log] {}/{}/{}/{}".format(e.status_code,
                                                                   e.request_id,
                                                                   e.error_code,
                                                                   e.error_msg))
            raise Exception("download_build_log failed")

    def show_step_output(self, project_id, pipeline_id, pipeline_run_id, step_run_ids):
        client = CodeArtsPipelineClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CodeArtsPipelineRegion.value_of("cn-north-4")) \
            .build()

        try:
            request = ShowStepOutputsRequest()
            request.project_id = project_id
            request.pipeline_id = pipeline_id
            request.pipeline_run_id = pipeline_run_id
            request.step_run_ids = step_run_ids
            response = client.show_step_outputs(request)
            return response.to_json_object()
        except exceptions.ClientRequestException as e:
            logging.info("[show_step_output] {}/{}/{}/{}".format(e.status_code,
                                                                 e.request_id,
                                                                 e.error_code,
                                                                 e.error_msg))
            raise Exception("show_step_output failed")

    def create_pipeline(self, project_id, pipeline_info, env_dict):
        """create pipeline
        :param project_id: the project id
        :param pipeline_info: user create_pipeline.json to dict_data
        :param env_dict: {
                "majun_domain": ma_jun_domain,
                "majun_token": ma_jun_token,
                "gitcode_api_domain": gitcode_api_domain,
                "gitcode_domain": gitcode_domain,
                "gitcode_token": gitcode_token,
                "ak": ak,
                "sk": sk,
                "obs_bucket_name": obs_bucket_name,
                "obs_endpoint": obs_endpoint,
                "obs_static_endpoint": obs_static_endpoint,
            }
        :return:
        """
        client = CodeArtsPipelineClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CodeArtsPipelineRegion.value_of("cn-north-4")) \
            .build()
        try:
            list_resources, list_envs = list(), list()

            for resources in pipeline_info["sources"]:
                code_source_params = CodeSourceParams(**resources["params"]
                )
                code_source = CodeSource(type=resources["type"], params=code_source_params)
                list_resources.append(code_source)

            for key, value in env_dict.items():
                custom_variable = CustomVariable(
                    name=key,
                    type="string",
                    value=value,
                    is_secret=True,
                    is_reset=False,
                    description="key",
                    is_runtime=False,
                )
                list_envs.append(custom_variable)

            pipeline_concurrency_mgmt = PipelineConcurrencyMgmt(
                concurrency_number=pipeline_info["concurrency_control"]["concurrency_number"],
                exceed_action=pipeline_info["concurrency_control"]["exceed_action"],
                enable=pipeline_info["concurrency_control"]["enable"]
            )

            request = CreatePipelineNewRequest(project_id=project_id)

            request.body = {
                "name": pipeline_info["name"],
                "description": pipeline_info["description"],
                "is_publish": pipeline_info["is_publish"],
                "sources": list_resources,
                "variables": list_envs,
                "triggers": pipeline_info["triggers"],
                "manifest_version": pipeline_info["manifest_version"],
                "definition": pipeline_info["definition"],
                "group_id": pipeline_info["group_id"],
                "concurrency_control": pipeline_concurrency_mgmt,
                "cancel_strategy": pipeline_info["cancel_strategy"]
            }
            response = client.create_pipeline_new(request)
            return response.to_json_object()
        except exceptions.ClientRequestException as e:
            logging.info("[create_pipeline] {}/{}/{}/{}".format(e.status_code,
                                                                e.request_id,
                                                                e.error_code,
                                                                e.error_msg))
            raise Exception("create_pipeline failed")

    def update_pipeline(self, project_id, pipeline_id, pipeline_info, env_dict):
        client = CodeArtsPipelineClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CodeArtsPipelineRegion.value_of("cn-north-4")) \
            .build()

        try:
            list_resources, list_envs = list(), list()

            for resources in pipeline_info["sources"]:
                code_source_params = CodeSourceParams(**resources["params"])
                code_source = CodeSource(type=resources["type"], params=code_source_params)
                list_resources.append(code_source)

            for key, value in env_dict.items():
                custom_variable = CustomVariable(
                    name=key,
                    type="string",
                    value=value,
                    is_secret=True,
                    is_reset=False,
                    description="key",
                    is_runtime=False,
                )
                list_envs.append(custom_variable)

            pipeline_concurrency_mgmt = PipelineConcurrencyMgmt(
                concurrency_number=pipeline_info["concurrency_control"]["concurrency_number"],
                exceed_action=pipeline_info["concurrency_control"]["exceed_action"],
                enable=pipeline_info["concurrency_control"]["enable"]
            )

            request = UpdatePipelineInfoRequest()
            request.project_id = project_id
            request.pipeline_id = pipeline_id
            print(pipeline_info)
            request.body = {
                "name": pipeline_info["name"],
                "description": pipeline_info["description"],
                "is_publish": pipeline_info["is_publish"],
                "sources": list_resources,
                "triggers": pipeline_info["triggers"],
                "manifest_version": pipeline_info["manifest_version"],
                "definition": pipeline_info["definition"],
                "group_id": pipeline_info["group_id"],
                "concurrency_control": pipeline_concurrency_mgmt,
                "cancel_strategy": pipeline_info["cancel_strategy"]
            }
            response = client.update_pipeline_info(request)
            print(response.to_json_object())
            return response.to_json_object()
        except exceptions.ClientRequestException as e:
            logging.info("[update_pipeline] {}/{}/{}/{}".format(e.status_code,
                                                                e.request_id,
                                                                e.error_code,
                                                                e.error_msg))
            raise Exception("update_pipeline failed")

    def get_pipeline_by_name(self, project_id, pipeline_name):
        """ get pipeline by name
        :param project_id: pipeline id
        :param pipeline_name: pipeline name
        :return:
        """
        client = CodeArtsPipelineClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CodeArtsPipelineRegion.value_of("cn-north-4")) \
            .build()

        try:
            request = ListPipelinesRequest()
            request.project_id = project_id
            request.body = ListPipelineQuery(
                name=pipeline_name
            )
            response = client.list_pipelines(request)
            return response.to_json_object()
        except exceptions.ClientRequestException as e:
            logging.info("[get_pipeline_by_name] {}/{}/{}/{}".format(e.status_code,
                                                                     e.request_id,
                                                                     e.error_code,
                                                                     e.error_msg))
            raise Exception("get_pipeline_by_name failed")

    def delete_pipeline(self, project_id, pipeline_id):
        """delete pipeline"""
        client = CodeArtsPipelineClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CodeArtsPipelineRegion.value_of("cn-north-4")) \
            .build()

        try:
            request = DeletePipelineRequest(project_id=project_id, pipeline_id=pipeline_id)
            response = client.delete_pipeline(request)
            return response.to_json_object()
        except exceptions.ClientRequestException as e:
            logging.info("[delete_pipeline] {}/{}/{}/{}".format(e.status_code,
                                                                e.request_id,
                                                                e.error_code,
                                                                e.error_msg))
            raise Exception("delete_pipeline failed")

    # noinspection PyProtectedMember
    def create_task(self, body):
        client = CodeArtsBuildClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CodeArtsBuildRegion.value_of("cn-north-4")) \
            .build()
        http_info = {
            "method": "POST",
            "resource_path": "/v1/job/create",
            "response_type": CreateBuildJobResponse,
            "body": body,
            "Content-Type": http_utils.select_header_content_type(['application/json'])
        }
        response = client._call_api(**http_info)
        return response.to_json_object()

    def get_task_by_name(self, project_id, job_name):
        index = 0
        try:
            while True:
                client = CodeArtsBuildClient.new_builder() \
                    .with_credentials(self._credentials) \
                    .with_region(CodeArtsBuildRegion.value_of("cn-north-4")) \
                    .build()
                request = ShowJobListByProjectIdRequest()
                request.page_index = index
                request.page_size = 100
                request.project_id = project_id
                response = client.show_job_list_by_project_id(request)
                for job in response.jobs:
                    if job.job_name == job_name:
                        return job.to_dict()
                else:
                    index += 1
                if index - 1 >= response.total / 100:
                    return None
        except exceptions.ClientRequestException as e:
            logging.info("[get_task_by_name] {}/{}/{}/{}".format(e.status_code,
                                                                 e.request_id,
                                                                 e.error_code,
                                                                 e.error_msg))
            raise Exception("get_task_by_name failed")

    def get_task_by_id(self, job_id):
        client = CodeArtsBuildClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CodeArtsBuildRegion.value_of("cn-north-4")) \
            .build()

        try:
            request = ListJobConfigRequest()
            request.job_id = job_id
            response = client.list_job_config(request)
            return response.to_json_object()
        except exceptions.ClientRequestException as e:
            logging.info("[get_task_by_id] {}/{}/{}/{}".format(e.status_code,
                                                               e.request_id,
                                                               e.error_code,
                                                               e.error_msg))
            raise Exception("get_task_by_id failed")

    def put_task_by_id(self, body):
        client = CodeArtsBuildClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CodeArtsBuildRegion.value_of("cn-north-4")) \
            .build()

        try:
            http_info = {
                "method": "POST",
                "resource_path": "/v1/job/update",
                "response_type": UpdateBuildJobResponse,
                "body": body,
                "Content-Type": http_utils.select_header_content_type(['application/json'])
            }
            response = client._call_api(**http_info)
            return response.to_json_object()
        except exceptions.ClientRequestException as e:
            logging.info("[put_task_by_id] {}/{}/{}/{}".format(e.status_code,
                                                               e.request_id,
                                                               e.error_code,
                                                               e.error_msg))
            raise Exception("put_task_by_id failed")

    def delete_task(self, job_id):
        client = CodeArtsBuildClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CodeArtsBuildRegion.value_of("cn-north-4")) \
            .build()

        try:
            request = DeleteBuildJobRequest(job_id=job_id)
            response = client.delete_build_job(request)
            return response.to_json_object()
        except exceptions.ClientRequestException as e:
            logging.info("[delete_task] {}/{}/{}/{}".format(e.status_code,
                                                            e.request_id,
                                                            e.error_code,
                                                            e.error_msg))
            raise Exception("delete_task failed")

    def set_variable(self, project_id, pipeline_id,variable_id):
        client = CodeArtsPipelineClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CodeArtsPipelineRegion.value_of("cn-north-4")) \
            .build()
        http_info = {
            "method": "POST",
            "resource_path": "/v5/{}/api/pipeline/variable/group/relation".format(project_id),
            "response_type": CreateBuildJobResponse,
            "body": {"pipeline_id": pipeline_id, "pipeline_group_ids": [variable_id]},
            "Content-Type": http_utils.select_header_content_type(['application/json'])
        }
        response = client._call_api(**http_info)
        return response.to_json_object()

    def get_webhook(self, project_id, pipeline_id):
        client = CodeArtsPipelineClient.new_builder() \
            .with_credentials(self._credentials) \
            .with_region(CodeArtsPipelineRegion.value_of("cn-north-4")) \
            .build()
        http_info = {
            "method": "GET",
            "resource_path": "/v5/{}/api/pipelines/{}/webhook".format(project_id, pipeline_id),
            "response_type": CreateBuildJobResponse,
        }
        response = client._call_api(**http_info)
        return response.to_json_object()